/*
 * Copyright (c) 2020 Nanjing Xiaoxiongpai Intelligent Technology Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"

#include "wifi_connect.h"
#include <queue.h>
#include <oc_mqtt_al.h>// 华为云MQTT适配层接口[6](@ref)
#include <oc_mqtt_profile.h>// 华为云物模型协议封装[5](@ref)
#include "E53_ST1.h"
#include <dtls_al.h>
#include <mqtt_al.h>

//nfc
#include "wifiiot_errno.h"
#include "wifiiot_gpio.h"
#include "wifiiot_gpio_ex.h"
#include "wifiiot_i2c.h"
#include "wifiiot_i2c_ex.h"
#include "nfc.h"
#include "NT3H.h"
#include "nfcForum.h"

//温湿度
#include "GXHT30.h"// 温湿度传感器驱动
//uart
#include "wifiiot_uart.h"

#define CONFIG_WIFI_SSID            "Galaxy"                       //修改为自己的WiFi 热点账号// 设备连接的WiFi SSID  "412"   "IQOO"     "oneplus"

#define CONFIG_WIFI_PWD              "galaxya54"              //修改为自己的WiFi 热点密码   "ljb08102817"    "200508102817"     "12345678"

// #define CONFIG_APP_SERVERIP       "121.36.42.100"                       //基础版平台对接地址

#define CONFIG_APP_SERVERIP       "117.78.5.125"                       //标准版平台对接地址

#define CONFIG_APP_SERVERPORT     "1883"

#define CONFIG_APP_DEVICEID       "680e302e84adf27cda5316b9_202021"       //替换为注册设备后生成的deviceid

#define CONFIG_APP_DEVICEPWD      "12345678"                                   //替换为注册设备后生成的密钥

#define CONFIG_APP_LIFETIME       60     ///< seconds

#define CONFIG_QUEUE_TIMEOUT      (5*1000)

#define MSGQUEUE_OBJECTS 16 // number of Message Queue Objects

/*ndef*/
#define NDEF_HEADER_SIZE 0x2 // NDEF协议的头部大小

#define UART_BUFF_SIZE 1000// UART缓冲区大小
/* 全局变量 */
uint8_t uart_buff[UART_BUFF_SIZE] = {0};// UART接收缓冲区
uint8_t *uart_buff_ptr = uart_buff;// 缓冲区指针

/* 消息类型定义 */
typedef enum
{
    en_msg_cmd = 0,// 命令类型消息
    en_msg_report,// 数据上报类型消息
}en_msg_type_t;
/* 命令消息结构体 */
typedef struct
{
    char *request_id;// 请求ID
    char *payload;// 消息内容
} cmd_t;
/* 上报数据结构体 */
typedef struct
{
    char Longitude [10];// 经度数据(格式：xxx.xxxxx)
    char Latitude  [9];// 纬度数据(格式：xx.xxxxx)
    char Humi[6]; // 湿度
    char Temp[6]; // 温度
} report_t;
/* 应用消息联合体 */
typedef struct
{
    en_msg_type_t msg_type;//消息类型标识
    union
    {
        cmd_t cmd; // 命令解析结构体
        report_t report;// 上报数据结构体
    } msg;
} app_msg_t;
/* 应用控制块 */
typedef struct
{
    queue_t *app_msg;// 消息队列ID// 消息队列指针，用于线程间通信
    int connected;// MQTT连接状态
    int beep;// 蜂鸣器状态
    int isReturn;// 退货标志
    char *productId; // 产品ID// 产品ID指针（通过UART获取）
} app_cb_t;
//唯一是全局的一个东西
static app_cb_t g_app_cb;// 全局应用控制块实例
/**
 * @brief 构造设备属性数据并上报华为云
 * @param report 包含GPS坐标和温湿度数据的结构体指针
 * 
 * 数据组织规范：
 * 1. 服务ID对应云平台定义的"Track"服务
 * 2. 使用链式结构组织多个属性字段
 * 3. 严格匹配物模型定义的属性名称和数据类型
 */
/*1. 属性上报处理*/
static void deal_report_msg(report_t *report)// 构造OC MQTT服务数据结构...
{
    oc_mqtt_profile_service_t service;// 服务主体定义
    oc_mqtt_profile_kv_t Longitude_value;
    oc_mqtt_profile_kv_t Latitude_value;
    oc_mqtt_profile_kv_t beep;
    oc_mqtt_profile_kv_t Humi;
    oc_mqtt_profile_kv_t Temp;
    oc_mqtt_profile_kv_t product;

    if(g_app_cb.connected != 1){
        return;
    }
 //设置服务的入口
    service.event_time = NULL;
    service.service_id = "Track";// 对应华为云产品模型中的服务ID[5](@ref)

    service.service_property = &Longitude_value;

    //service.service_property = &beep;

    service.nxt = NULL;

  //填写第一个数据，并指向下一个数据
    Longitude_value.key = "Longitude"; // 物模型属性字段
    Longitude_value.value = &report->Longitude;
    Longitude_value.type = EN_OC_MQTT_PROFILE_VALUE_STRING;// 字符串类型
    Longitude_value.nxt = &Latitude_value;
//同上，填写第二个数据
    Latitude_value.key = "Latitude";
    Latitude_value.value = &report->Latitude;
    Latitude_value.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    Latitude_value.nxt = &beep;
 
    beep.key = "BeepStatus";
    beep.value = g_app_cb.beep ? "ON" : "OFF";
    beep.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    beep.nxt = &Humi;

//湿度
    Humi.key = "Humidity";
    Humi.value = &report->Humi;
    Humi.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    Humi.nxt = &Temp;

//温度
    Temp.key = "Temperature";
    Temp.value = &report->Temp;
    Temp.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    Temp.nxt = &product;

    product.key = "productID";
    product.value = g_app_cb.productId;
    product.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    product.nxt = NULL;



   //oc_mqtt_profile_propertyreport(NULL,&service);// 调用SDK上报接口[6](@ref)
   int ret = oc_mqtt_profile_propertyreport(NULL, &service);
    if (ret == 0)
    {
       printf("successfully report\r\n");
    }
    return;
}
/**
 * @brief MQTT消息接收回调函数
 * @param msg 云平台下发的消息结构体
 * @return 处理结果（0-成功 其他-错误码）
 * 
 * 功能说明：
 * 1. 验证消息有效性（非空检查）
 * 2. 解析请求ID和payload
 * 3. 将消息存入处理队列异步处理
 * 
 * 内存管理：
 * 使用malloc动态分配内存，需在后续处理中确保释放
 */
/*2. MQTT消息接收回调*/
//use this function to push all the message to the buffer
static int msg_rcv_callback(oc_mqtt_profile_msgrcv_t *msg)// 解析消息并放入消息队列
{
    int    ret = 0;
    char  *buf;
    int    buf_len;
    app_msg_t *app_msg;

    if((NULL == msg)|| (msg->request_id == NULL) || (msg->type != EN_OC_MQTT_PROFILE_MSG_TYPE_DOWN_COMMANDS)){
        return ret;
    }

    buf_len = sizeof(app_msg_t) + strlen(msg->request_id) + 1 + msg->msg_len + 1;
    buf = malloc(buf_len);
    if(NULL == buf){
        return ret;
    }
    app_msg = (app_msg_t *)buf;
    buf += sizeof(app_msg_t);

    app_msg->msg_type = en_msg_cmd;
    app_msg->msg.cmd.request_id = buf;
    buf_len = strlen(msg->request_id);
    buf += buf_len + 1;
    memcpy(app_msg->msg.cmd.request_id, msg->request_id, buf_len);// 拷贝原始消息
    app_msg->msg.cmd.request_id[buf_len] = '\0';

    buf_len = msg->msg_len;
    app_msg->msg.cmd.payload = buf;
    memcpy(app_msg->msg.cmd.payload, msg->msg, buf_len);
    app_msg->msg.cmd.payload[buf_len] = '\0';

    ret = queue_push(g_app_cb.app_msg,app_msg,10);// 将消息压入队列异步处理
    if(ret != 0){
        free(app_msg);
    }

    return ret;
}
/*构造响应结构并发送*/
/**/

/*3. 命令解析与执行*/
///< COMMAND DEAL
#include <cJSON.h>
static void deal_cmd_msg(cmd_t *cmd)// 解析JSON命令并执行对应操作
{
    cJSON *obj_root;
    cJSON *obj_cmdname;
    cJSON *obj_paras;
    cJSON *obj_para;

    int cmdret = 1;
    oc_mqtt_profile_cmdresp_t cmdresp;
    obj_root = cJSON_Parse(cmd->payload);// 解析JSON格式命令
    if (NULL == obj_root)
    {
        goto EXIT_JSONPARSE;
    }

    obj_cmdname = cJSON_GetObjectItem(obj_root, "command_name");
    if (NULL == obj_cmdname)
    {
        goto EXIT_CMDOBJ;
    }
    //判断是否要报警
    if (0 == strcmp(cJSON_GetStringValue(obj_cmdname), "Track_Control_Beep"))
    {
        obj_paras = cJSON_GetObjectItem(obj_root, "paras");
        if (NULL == obj_paras)
        {
            goto EXIT_OBJPARAS;
        }
        obj_para = cJSON_GetObjectItem(obj_paras, "Beep");
        if (NULL == obj_para)
        {
            goto EXIT_OBJPARA;
        }
        //< operate the Beep here// 解析蜂鸣器控制命令
        if (0 == strcmp(cJSON_GetStringValue(obj_para), "ON"))
        {
            g_app_cb.beep = 1;
            Beep_StatusSet(ON); // 调用硬件控制接口
            printf("Beep On!\r\n");
        }
        else
        {
            g_app_cb.beep = 0;
            Beep_StatusSet(OFF);
            printf("Beep Off!\r\n");
        }
        cmdret = 0;
    }
     //判断是否要退货
    if (strcmp(cJSON_GetStringValue(obj_cmdname), "Return_Package") == 0) {
        obj_paras = cJSON_GetObjectItem(obj_root, "paras");
        if (obj_paras == NULL) {
            cJSON_Delete(obj_root);
        }
        obj_para = cJSON_GetObjectItem(obj_paras, "return"); 
        if (obj_para == NULL) {
            cJSON_Delete(obj_root);
        }
        ///< operate the Beep here
        if (strcmp(cJSON_GetStringValue(obj_para), "True") == 0) {
            printf("Return Package!\r\n");
            g_app_cb.isReturn = 1;
            
        } else {
            printf("Cancel return Package!\r\n");
            g_app_cb.isReturn = 0;
           
        }
        cmdret = 0;
        //oc_cmdresp(cmd, cmdret);
    }
EXIT_OBJPARA:
EXIT_OBJPARAS:
EXIT_CMDOBJ:
    cJSON_Delete(obj_root);// 释放JSON对象内存
EXIT_JSONPARSE:
    ///< do the response
    cmdresp.paras = NULL;
    cmdresp.request_id = cmd->request_id;
    cmdresp.ret_code = cmdret;
    cmdresp.ret_name = NULL;
    (void)oc_mqtt_profile_cmdresp(NULL, &cmdresp);// 发送命令响应
    return;
}
/**
 * @brief 主任务入口函数
 * 
 * 执行流程：
 * 1. 连接WiFi（SSID和密码来自宏定义）
 * 2. 初始化DTLS和MQTT协议栈
 * 3. 建立华为云IoT平台连接
 * 4. 进入消息循环处理队列消息
 * 
 * 异常处理：
 * - 连接失败时打印错误日志
 * - 消息队列操作添加超时机制
 */
/*四、主任务流程*/
static int task_main_entry(void)// WiFi连接/MQTT初始化/消息循环
{
    app_msg_t *app_msg;
    uint32_t ret ;

    WifiConnect(CONFIG_WIFI_SSID, CONFIG_WIFI_PWD); // 连接WiFi
    dtls_al_init();// 初始化安全传输层
    mqtt_al_init();
    oc_mqtt_init();
    
    g_app_cb.app_msg = queue_create("queue_rcvmsg",10,1);
    if(NULL ==  g_app_cb.app_msg){
        printf("Create receive msg queue failed");
        
    }
    oc_mqtt_profile_connect_t  connect_para;// 连接华为云平台
    (void) memset( &connect_para, 0, sizeof(connect_para));

    connect_para.boostrap =      0;
    connect_para.device_id =     CONFIG_APP_DEVICEID;
    connect_para.device_passwd = CONFIG_APP_DEVICEPWD;
    connect_para.server_addr =   CONFIG_APP_SERVERIP;
    connect_para.server_port =   CONFIG_APP_SERVERPORT;
    connect_para.life_time =     CONFIG_APP_LIFETIME;
    connect_para.rcvfunc =       msg_rcv_callback;
    connect_para.security.type = EN_DTLS_AL_SECURITY_TYPE_NONE;
    ret = oc_mqtt_profile_connect(&connect_para);
    if((ret == (int)en_oc_mqtt_err_ok)){
        g_app_cb.connected = 1;
        printf("oc_mqtt_profile_connect succed!\r\n");
    }
    else
    {
        printf("oc_mqtt_profile_connect faild!\r\n");
    }
    while (1)
    {
        app_msg = NULL;
        (void)queue_pop(g_app_cb.app_msg,(void **)&app_msg,0xFFFFFFFF);// 阻塞式获取消息
        if (NULL != app_msg)// 消息路由处理
        {
            switch (app_msg->msg_type) 
            {
            case en_msg_cmd:            // 命令处理分支
                deal_cmd_msg(&app_msg->msg.cmd);
                break;
            case en_msg_report:
                deal_report_msg(&app_msg->msg.report);// 数据上报分支
                break;
            default:
                break;
            }
            free(app_msg);
        }
    }
    return 0;
}

/*五、传感器数据采集*/
static int task_sensor_entry(void)// 读取GPS和温湿度数据/上报消息
{
    E53_ST1_Data_TypeDef data;
    E53_IA1_Data_TypeDef data1;
    app_msg_t *app_msg;
    //进行GPS模块的初始化
    Init_E53_ST1();
    //进行温湿度传感器的初始化
    E53_IA1_Init();
    /* int ret = GXHT_INIT();
    if (ret != 0) {
        printf("Init GXHT30 failed!\r\n");
        //return 0;
    } */
    //进入传感器探测循环，读取数据

    while (1)
    {
         //GPS读数，处理数据
        E53_ST1_Read_Data(&data);// 读取GPS模块数据
        printf("\r\n******************************Longitude Value is  %.5f\r\n", data.Longitude);
		printf("\r\n******************************Latitude Value is  %.5f\r\n", data.Latitude);

        //温湿度传感器读数
        E53_IA1_Read_Data(&data1);
        printf("Temp = %.2f, humi = %.2f\r\n",data1.Temperature,data1.Humidity);
        //判断是都需要报警
        if(data1.Temperature>40 || g_app_cb.beep == 1){
            Beep_StatusSet(ON);
            g_app_cb.beep = 1;//更新状态，报警之后只能手动关闭
        }else{
            Beep_StatusSet(OFF);
            g_app_cb.beep = 0;//更新状态
        }

        //===================================    
        //创建消息队列
       app_msg = malloc(sizeof(app_msg_t));
        if ((NULL != app_msg) & (data.Longitude != 0) & (data.Latitude != 0))//只有全不为0的时候才上报
        {
            app_msg->msg_type = en_msg_report;
            sprintf(app_msg->msg.report.Longitude,"%.5f\0",data.Longitude);// 格式化数据
            sprintf(app_msg->msg.report.Latitude,"%.5f\0",data.Latitude);

            sprintf(app_msg->msg.report.Humi, "%.2f\0", data1.Humidity);
            sprintf(app_msg->msg.report.Temp, "%.2f\0", data1.Temperature);
            if(0 != queue_push(g_app_cb.app_msg,app_msg,CONFIG_QUEUE_TIMEOUT)){// 提交上报队列
                free(app_msg);
            }
        }
        sleep(3);
    }
    return 0;
}
/**
 * @brief NFC模块初始化
 * 
 * 硬件配置：
 * - GPIO_14：状态指示灯
 * - I2C1：NFC通信接口（GPIO_0作为SDA，GPIO_1作为SCL）
 * 
 * 操作流程：
 * 1. 初始化GPIO和I2C控制器
 * 2. 设置I2C波特率为400kbps
 * 3. 循环读取NFC标签直到验证通过
 */
/*LED控制任务（根据退货状态闪烁）*/
static void LED_Blink_Task(void)// GPIO控制LED闪烁逻辑...
{
    
    //add
    // 初始化GPIO（LED）
    GpioInit();
    //设置GPIO_14的复用功能为普通GPIO
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_14,WIFI_IOT_IO_FUNC_GPIO_14_GPIO);
    //设置GPIO_14为输出模式
    GpioSetDir(WIFI_IOT_GPIO_IDX_14, WIFI_IOT_GPIO_DIR_OUT);
    //闪烁提示灯
    //InfoLEDFlash();


    while (1) {
        if (!g_app_cb.isReturn)//正常运输，不退货
        {
            GpioSetOutputVal(WIFI_IOT_GPIO_IDX_14, 1);
            sleep(1);
            GpioSetOutputVal(WIFI_IOT_GPIO_IDX_14, 0);
            sleep(1);
        }else{//常亮表示要退货
            GpioSetOutputVal(WIFI_IOT_GPIO_IDX_14, 0);
            sleep(1);
        }
    }
}
 /*LED提示灯闪烁（用于初始化提示）*/
 static void InfoLEDFlash(void)// 快速闪烁10次...
{
    int i=10;
    while (i-->0)
    {
        ////设置GPIO_14输出高电平点亮LED灯
        GpioSetOutputVal(WIFI_IOT_GPIO_IDX_14, 1);

        // delay 1s
        usleep(100000);

        //设置GPIO_14输出低电平熄灭LED灯
        GpioSetOutputVal(WIFI_IOT_GPIO_IDX_14, 0);

        // delay 1s
        usleep(100000);
    }
} 


/*nfc初始化*/
static void NFC_I2C_INIT(void)
{
    // 初始化GPIO（LED）
    GpioInit();
    //设置GPIO_14的复用功能为普通GPIO
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_14,WIFI_IOT_IO_FUNC_GPIO_14_GPIO);
    //设置GPIO_14为输出模式
    GpioSetDir(WIFI_IOT_GPIO_IDX_14, WIFI_IOT_GPIO_DIR_OUT);
    //闪烁提示灯
    InfoLEDFlash();

    //初始化I2C
    GpioInit();
    // GPIO_10复用为I2C1_SDA
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_0, WIFI_IOT_IO_FUNC_GPIO_0_I2C1_SDA);
    //GPIO_9复用为I2C1_SCL
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_1, WIFI_IOT_IO_FUNC_GPIO_1_I2C1_SCL);
    //baudrate: 400kbps
    I2cInit(WIFI_IOT_I2C_IDX_1, 400000);
    I2cSetBaudrate(WIFI_IOT_I2C_IDX_1, 400000);

    printf("I2C Test Start\n");
} 


static void NFC_confirm_message(void)
{
    uint8_t ret;
    uint8_t memory_buf[16*15];// NT3H1101用户页存储结构（15页×16字节）
    int flag=1;
    NFC_I2C_INIT();
    while (flag==1)
    {
        NT3H1101_Read_Userpages(15, memory_buf);
        printf("Read Data: %s \r\n",memory_buf);
        if (strstr((char*)memory_buf,"123456"))
        {
            flag=0;
            NT3HEraseAllTag();
            break;
        }
        sleep(2);
        
    }
    InfoLEDFlash();
}

/**
 * @brief UART2初始化（用于接收产品ID）
 */
static void UART2INIT(void) // 配置UART参数/接收产品ID数据...
{
    //初始化GPS和串口1
    Init_E53_ST1();
    //初始化串口2
    uint8_t ret;
    
    //初始化I2C
    GpioInit();
    // GPIO_0复用为I2C1_SDA
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_11, WIFI_IOT_IO_FUNC_GPIO_11_UART2_TXD);
    //GPIO_1复用为I2C1_SCL
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_12, WIFI_IOT_IO_FUNC_GPIO_12_UART2_RXD);
    WifiIotUartAttribute uart_attr = {

        // baud_rate: 9600
        .baudRate = 9600,

        // data_bits: 8bits
        .dataBits = 8,
        .stopBits = 1,
        .parity = 0,
    };

    // Initialize uart driver
    ret = UartInit(WIFI_IOT_UART_IDX_2, &uart_attr, NULL);//GPIO_11,12
    if (ret != WIFI_IOT_SUCCESS) {
        printf("Failed to init uart2! Err code = %d\n", ret);
        //return;
    }
    else{
        printf("init uart2 success!");
    }
    //printf("UART Test Start\n");
    while (1) {
        // receive data through uart2
        UartRead(WIFI_IOT_UART_IDX_2, uart_buff_ptr, UART_BUFF_SIZE);
        printf("Uart2 read data:%s\n", uart_buff_ptr);
        if(uart_buff[2]){//only if received data : break
            g_app_cb.productId = (char*)uart_buff_ptr;
            break;
        }
        usleep(1000000/2);
    }
    InfoLEDFlash();
    
}


static void POWERINIT(void)
{
     // 初始化GPIO
     GpioInit();
     //设置GPIO_的复用功能为普通GPIO
     IoSetFunc(WIFI_IOT_IO_NAME_GPIO_13,WIFI_IOT_IO_FUNC_GPIO_13_GPIO);
     //设置GPIO_9为输出模式
     GpioSetDir(WIFI_IOT_GPIO_IDX_13, WIFI_IOT_GPIO_DIR_OUT);

     GpioSetOutputVal(WIFI_IOT_GPIO_IDX_13, 1);

     // 初始化GPIO
     GpioInit();
     //设置GPIO_4的复用功能为普通GPIO
     IoSetFunc(WIFI_IOT_IO_NAME_GPIO_7,WIFI_IOT_IO_FUNC_GPIO_7_GPIO);
     //设置GPIO_4为输出模式
     GpioSetDir(WIFI_IOT_GPIO_IDX_7, WIFI_IOT_GPIO_DIR_OUT);

     GpioSetOutputVal(WIFI_IOT_GPIO_IDX_7, 0);


}

/*六、系统初始化*/
static void OC_Demo(void)
{
    //设置一些基本参数
    g_app_cb.beep = 0;
    g_app_cb.isReturn = 0;
    g_app_cb.productId = "product";


    //上电先进行检验
    NFC_I2C_INIT();
    //确认到NFC数据后才继续
    NFC_confirm_message();
    //检测到商品信息才继续
    UART2INIT();
    //GXTH30高电平
    POWERINIT();
    //进入主函数，创建线程
    osThreadAttr_t attr;

    attr.name = "task_main_entry";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = 24;

    if (osThreadNew((osThreadFunc_t)task_main_entry, NULL, &attr) == NULL)// 创建主任务线程（优先级24）
    {
        printf("Falied to create task_main_entry!\n");
    }
    attr.stack_size = 4096;
    attr.priority = 25;
    attr.name = "task_sensor_entry";
    if (osThreadNew((osThreadFunc_t)task_sensor_entry, NULL, &attr) == NULL)// 创建传感器任务
    {
        printf("Falied to create task_sensor_entry!\n");
    }
    attr.stack_size = 4096;
    attr.priority = 26;
    attr.name = "LED_Blink_Task";
    if (osThreadNew((osThreadFunc_t)LED_Blink_Task, NULL, &attr) == NULL) {
        printf("Failed to create LedBlinkTaskEntry!\n");
    }

}

APP_FEATURE_INIT(OC_Demo);